package com.k.kpp.log4j2;

import java.io.File;
import java.io.FileNotFoundException;
import java.io.IOException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.xpath.XPath;
import javax.xml.xpath.XPathConstants;
import javax.xml.xpath.XPathExpressionException;
import javax.xml.xpath.XPathFactory;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;
import org.springframework.util.ResourceUtils;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;

/**
 * @author KK
 */
@Service
public class LogConfigServiceImpl implements LogConfigService {
    /** LOGGER */
    private static final Logger LOGGER = LoggerFactory.getLogger(LogConfigServiceImpl.class);

    /** DEFAULT VALUES */
    private static final String DEFAULT_LOG_SAVE_PATH="C:/ProgramData/Logs";
    private static final String DEFAULT_LOG_SAVE_DAYS="30";
    private static final String DEFAULT_LOG_LEVEL="5";
    private static final String DEFAULT_LOG_SIZE="10";
    private static final String DEFAULT_LOG_ENABLE_COMPRESS ="1";
    private static final String DEFAULT_LOG_CONFIG_FILENAME ="1";

    @Override
    public void update(LogConfigVo vo) {
        LOGGER.debug("LogConfigServiceImpl update:{}",vo);
        modifyXmlConfig(vo);
    }

    @Override
    public void updateDefault() {
        LogConfigVo vo = new LogConfigVo(DEFAULT_LOG_CONFIG_FILENAME,
                DEFAULT_LOG_SAVE_PATH,
                DEFAULT_LOG_SAVE_DAYS,
                DEFAULT_LOG_LEVEL,
                DEFAULT_LOG_SIZE,
                DEFAULT_LOG_ENABLE_COMPRESS);
        modifyXmlConfig(vo);
    }

    private void modifyXmlConfig(LogConfigVo vo) {
        String pathPrefix;
        String configFileName = vo.getLogConfigFileName();
        String configFilePath = "";
        try {
            pathPrefix = ResourceUtils.getURL("classpath:").getPath();
            configFilePath = pathPrefix + configFileName;
        } catch (FileNotFoundException e1) {
            LOGGER.error("get log4j2.xml error",e1);
        }
        LOGGER.debug("configFilePath:{}", configFilePath);
        DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
        DocumentBuilder db;
        try {
            db = dbf.newDocumentBuilder();
            Document doc = db.parse(configFilePath);
            Element root = doc.getDocumentElement();
            // 修改相应节点
            if (validateValue(vo.getLogSaveDir())) {
                modifyLogSaveDir(root, vo);
            }
            if (validateValue(vo.getLogLevel())) {
                modifyLogLevel(root, vo.getLogLevel());
            }
            if (validateValue(vo.getLogSaveDays())) {
                modifyLogSaveDays(root, vo.getLogSaveDays());
            }
            // 保存
            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer former = factory.newTransformer();
            former.transform(new DOMSource(doc), new StreamResult(new File(configFilePath)));
            //
        } catch (ParserConfigurationException e) {
            e.printStackTrace();
        } catch (SAXException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (TransformerException e) {
            e.printStackTrace();
        }
    }

    private boolean validateValue(String value) {
        boolean bl = false;
        if (value == null || "null".equals(value) || "".equals(value)) {
        } else {
            bl = true;
        }
        return bl;
    }

    /**
     * 修改日志保存目录
     * @param root root
     * @param vo dir
     */
    private void modifyLogSaveDir(Element root, LogConfigVo vo) {
        LOGGER.error("modifyLogSaveDir:" + vo.getLogSaveDir());
        LOGGER.error("modifyLogSaveDir root:" + root);
        Element ele = (Element) selectSingleNode("/Configuration/Properties/Property[@name='logDir']", root);
        ele.setTextContent(vo.getLogSaveDir());
    }

    /**
     * 修改日志保存天数
     * @param root root
     * @param logSaveDays days
     */
    private void modifyLogSaveDays(Element root, String logSaveDays) {
        Element ele = (Element) selectSingleNode("/Configuration/Properties/Property[@name='logSaveDays']", root);
        ele.setTextContent(logSaveDays + "d");
    }


    /**
     * 修改日志级别
     * @param root root
     * @param levelStr level
     */
    private void modifyLogLevel(Element root, String levelStr) {
        LOGGER.error("modifyLogLevel root:" + root);
        LOGGER.error("modifyLogLevel level:" + levelStr);
        int levelIntSLF = pageLevelConvert(Integer.parseInt(levelStr));
        String levelStrSLF = LogLevel.findStrByInt(levelIntSLF);
        // log4j2
        Element ele = (Element) selectSingleNode("/Configuration/Loggers/Root", root);
        ele.setAttribute("level", levelStrSLF);
        ele.setAttribute("level", levelStrSLF);
    }

    private int pageLevelConvert(int level) {
        int levelConvert = 1;
        switch (level) {
            case 1:
                levelConvert = 4;
                break;
            case 2:
                levelConvert = 4;
                break;
            case 3:
                levelConvert = 3;
                break;
            case 4:
                levelConvert = 2;
                break;
            case 5:
                levelConvert = 1;
                break;
            default:
                levelConvert = 1;
                break;
        }
        return levelConvert;
    }


    private static Node selectSingleNode(String express, Element source) {
        Node result = null;
        XPathFactory xpathFactory = XPathFactory.newInstance();
        XPath xpath = xpathFactory.newXPath();
        try {
            result = (Node) xpath.evaluate(express, source, XPathConstants.NODE);
            System.out.println(result);
        } catch (XPathExpressionException e) {
            System.out.println(e.getMessage());
        }
        return result;
    }

}
